package com.luzh.myspring.beans.factory.support;

import cn.hutool.core.util.StrUtil;
import com.luzh.myspring.beans.BeanException;
import com.luzh.myspring.beans.factory.FactoryBean;
import com.luzh.myspring.beans.factory.config.BeanDefinition;
import com.luzh.myspring.beans.factory.config.BeanPostProcessor;
import com.luzh.myspring.beans.factory.config.ConfigurableBeanFactory;
import com.luzh.myspring.core.convert.ConversionService;
import com.luzh.myspring.util.StringValueResolver;

import java.util.ArrayList;
import java.util.List;

/**
 * 可配置的bean容器（获取bean，bean处理器，字符串解析器，类型转换服务）
 */
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport  implements ConfigurableBeanFactory {

    private final ClassLoader beanClassLoader = Thread.currentThread().getContextClassLoader();
    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
    private final List<StringValueResolver> embeddedValueResolvers = new ArrayList<>();
    private ConversionService conversionService;

    @Override
    public void setConversionService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }
    @Override
    public ConversionService getConversionService() {
        return conversionService;
    }

    public ClassLoader getBeanClassLoader() {
        return this.beanClassLoader;
    }


    @Override
    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor){
        //同类的处理器只能有一个
        this.beanPostProcessors.remove(beanPostProcessor);
        this.beanPostProcessors.add(beanPostProcessor);
    }
    public List<BeanPostProcessor> getBeanPostProcessors() {
        return this.beanPostProcessors;
    }


    @Override
    public void addEmbeddedValueResolver(StringValueResolver valueResolver) {
        embeddedValueResolvers.add(valueResolver);
    }

    /**
     * 解析字符串
     * @param value
     * @return
     */
    @Override
    public String resolveEmbeddedValue(String value) {
        String result = value;
        for (StringValueResolver resolver : embeddedValueResolvers) {
            result = resolver.resolveStringValue(result);
        }
        return result;
    }

    @Override
    public boolean containsBean(String name) {
        return containsBeanDefinition(name);
    }

    @Override
    public Object getBean(String beanName) throws BeanException {
        return doGetBean(beanName, null);
    }
    @Override
    public Object getBean(String beanName, Object... args) throws BeanException {
        return doGetBean(beanName, args);
    }
    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeanException {
        return (T) getBean(name);
    }
    /**
     * 模板模式，获取bean实例
     * @param beanName
     * @return
     * @throws BeanException
     */
    protected <T> T doGetBean(final String beanName, final Object[] args) {
        if(StrUtil.isEmpty(beanName))
            throw new BeanException("Bean name is empty");
        //先查缓存
        Object bean = getSingleton(beanName);
        if (bean != null) {
            return (T) getObjectForBeanInstance(bean,beanName);
        }
        //没有就根据定义创建
        BeanDefinition beanDefinition = getBeanDefinition(beanName);
        bean=createBean(beanName, beanDefinition, args);
        return (T) getObjectForBeanInstance(bean,beanName);
    }

    /**
     * 从实例或者工厂实例中获取bean
     * @param beanInstance
     * @param beanName
     * @return
     */
    private Object getObjectForBeanInstance(Object beanInstance, String beanName) {
        //非工厂bean直接返回
        if (!(beanInstance instanceof FactoryBean)) {
            return beanInstance;
        }
        //查询工厂单例缓存
        Object object = getCachedObjectForFactoryBean(beanName);
        //直接创建
        if (object == null) {
            FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance;
            object = getObjectFromFactoryBean(factoryBean, beanName);
        }
        return object;
    }

    protected abstract boolean containsBeanDefinition(String beanName);
    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeanException;
    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeanException;
}
